# Lambdas with object instances

require 'benchmark'
require 'rubygems'
require 'metaid'
require 'profile'

class Proc
  def tag
    'proc_' + object_id.to_s(36).sub('-', '_')
  end
end

class Object
  def const_tag
    'C' + object_id.to_s(36).upcase.sub('-', '_')
  end
end

class C
  attr_accessor :name, :reverse
  
  def initialize(n)
    @name = n
    @reverse = @name.reverse
  end

  def self.define_proc(&block)
    tag = block.tag
    class_def(tag.to_sym, &block) unless instance_methods.include?(tag)
    tag.to_sym
  end
  
  def self.cache_constant(value)
    tag = value.const_tag
    class_eval "#{tag} = #{value.inspect}.freeze" rescue nil
    tag
  end
  
  def self.route(rule, &block)
    @@rules ||= []
    @@rules.unshift [rule, block]
    compile_rules 
  end
  
  def self.rule_to_statement(rule, block)
    tag = define_proc(&block)
    if Proc === rule
      cond = define_proc(&rule).to_s
    else
#      cond = rule.to_a.map {|kv|
#        "(@#{kv[0]} =~ #{Regexp.new(kv[1]).inspect})"}.join('&&')
      cond = rule.to_a.map {|kv|
        value_tag = cache_constant(Regexp.new(kv[1]))
        "(@#{kv[0]} =~ #{value_tag})"}.join('&&')
    end
    "return #{tag} if #{cond}\n"
  end
  
  def self.compile_rules
    code = "lambda {"
    @@rules.each {|r| code << rule_to_statement(r[0], r[1])}
    code += "}"
    class_def(:process, &eval(code))
  end
  
  def t1
    return "#{name} h" if name =~ /^h/
    return "#{name} h" if (name =~ /^g/) && (reverse =~ /a$/)
    return "#{name} h" if name =~ /^f/
    return "#{name} h" if name =~ /^e/
    return "#{name} h" if name =~ /^d/
    return "#{name} h" if name =~ /^c/
    return "#{name} h" if name =~ /^b/
    return "#{name} h" if name =~ /^a/
    return "#{name} schatzie" if name =~ /^z/
    return "#{name} mau" if name =~ /^s/ 
  end
end

C.route(:name => '^s') {"#{name} mau"}
C.route(:name => '^z') {"#{name} schatzie"}
C.route(:name => '^a') {"#{name} a"}
C.route(:name => '^b') {"#{name} b"}
C.route(:name => '^c') {"#{name} c"}
C.route(:name => '^d') {"#{name} d"}
C.route(:name => '^e') {"#{name} e"}
C.route(:name => '^f') {"#{name} f"}
C.route(:name => '^g', :reverse => 'a$') {"#{name} g"}
C.route(:name => '^h') {"#{name} h"}

def assert(c)
#  raise "Assertion failed" if !c
end

def test(n = 5000)
  c1 = C.new('sharon')
  c2 = C.new('hag')
  n.times {assert c1.process == "sharon mau"}
  n.times {assert c2.process == "hag h"}
end

test
